package com.zhu.classfile.attribute;

import com.zhu.classfile.ClassReader;

import java.nio.ByteBuffer;

/**
 * LineNumberTable 属性
 * 用于将字节码偏移（pc）映射到源文件行号
 *
 * 结构：
 * LineNumberTable_attribute {
 *     u2 attribute_name_index;
 *     u4 attribute_length;
 *     u2 line_number_table_length;
 *     { u2 start_pc; u2 line_number; } line_number_table[line_number_table_length];
 * }
 */
public class LineNumberTableAttribute implements AttributeInfo {

    private LineNumberTableEntry[] lineNumberTable;

    @Override
    public void readInfo(ByteBuffer reader) {
        int tableLength = Short.toUnsignedInt(reader.getShort());
        lineNumberTable = new LineNumberTableEntry[tableLength];
        for (int i = 0; i < tableLength; i++) {
            lineNumberTable[i] = new LineNumberTableEntry(
                    Short.toUnsignedInt(reader.getShort()), // start_pc
                    Short.toUnsignedInt(reader.getShort())  // line_number
            );
        }
    }

    /**
     * 根据字节码偏移（pc）查找对应的源码行号
     * 使用“最右匹配”策略：找到最后一个满足 pc >= start_pc 的 entry
     *
     * @param pc 字节码程序计数器
     * @return 对应的行号，未找到返回 -1
     */
    public int getLineNumber(int pc) {
        // 从后往前找，确保找到的是最新的有效映射
        for (int i = lineNumberTable.length - 1; i >= 0; i--) {
            LineNumberTableEntry entry = lineNumberTable[i];
            if (pc >= entry.getStartPc()) {
                return entry.getLineNumber();
            }
        }
        return -1;
    }

    // ------------------------
    // 内部静态类：不可变数据结构
    // ------------------------

    public static class LineNumberTableEntry {
        private final int startPc;
        private final int lineNumber;

        public LineNumberTableEntry(int startPc, int lineNumber) {
            this.startPc = startPc;
            this.lineNumber = lineNumber;
        }

        public int getStartPc() {
            return startPc;
        }

        public int getLineNumber() {
            return lineNumber;
        }

        @Override
        public String toString() {
            return String.format("LineNumberEntry[startPc=%d, line=%d]", startPc, lineNumber);
        }
    }
}

